package jocl.samples;

import org.jocl.*;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static org.jocl.CL.*;
import static org.jocl.CL.clGetDeviceIDs;

/**
 * Created with IntelliJ IDEA.
 * User: Duy Chung
 * Date: 9/15/13
 * Time: 10:49 PM
 * To change this template use File | Settings | File Templates.
 */
public class MultiDeviceComputing {

    private String programSource =
            "__kernel void sampleKernel(__global const float *input,"+
                    "                           __global float *output, " +
                    "                           int size)"+
                    "{"+
                    "    int gid = get_global_id(0);"+
                    "    output[gid] = 0;" +
                    "    for (int i=0; i<size; i++) " +
                    "        output[gid] += input[i];" +
                    "}";

    List<Computation> threads = new ArrayList<Computation>();

    private void compute(int n) {
        // find all devices;
        // The platform and device type that will be used
        //final int platformIndex = 0;
        final long deviceType = CL_DEVICE_TYPE_ALL;

        // Enable exceptions and subsequently omit error checks in this sample
        CL.setExceptionsEnabled(true);

        // Obtain the number of platforms
        int numPlatformsArray[] = new int[1];
        clGetPlatformIDs(0, null, numPlatformsArray);
        int numPlatforms = numPlatformsArray[0];

        // Obtain a platform ID
        cl_platform_id platforms[] = new cl_platform_id[numPlatforms];
        clGetPlatformIDs(platforms.length, platforms, null);
        
        
        // Execute the kernel on each command queue, and
        // create events for each kernel launch
        ExecutorService executor = Executors.newFixedThreadPool(10);
        List<Runnable> list = new ArrayList<Runnable>(0);
        
        // iterate all platforms
		for (int p = platforms.length - 1; p >= 0; p--) {

			cl_platform_id platform = platforms[p];
//			String platformName = getString(platform, CL_PLATFORM_NAME);
			// System.out.println("Using platform "+platformIndex+" of "+
			// numPlatforms+": "+platformName);

			// Initialize the context properties
			cl_context_properties contextProperties = new cl_context_properties();
			contextProperties.addProperty(CL_CONTEXT_PLATFORM, platform);

			// Obtain the number of devices for the platform
			int numDevicesArray[] = new int[1];
			clGetDeviceIDs(platform, deviceType, 0, null, numDevicesArray);
			int numDevices = numDevicesArray[0];

			// Obtain a device IDs
			cl_device_id devices[] = new cl_device_id[numDevices];
			clGetDeviceIDs(platform, deviceType, numDevices, devices, null);

			// distribute workload for all devices in all platforms
			for (int i = 0; i < devices.length; i++) {
				// create context, program, kernel, command for each device
				// Create and build the program and the kernel
				cl_context context = clCreateContext(null, 1,
						new cl_device_id[] { devices[i] }, null, null, null);
				cl_program program = clCreateProgramWithSource(context, 1,
						new String[] { programSource }, null, null);
				clBuildProgram(program, 0, null, null, null, null);
				Runnable worker = new Computation(n, devices.length, context, program, devices[i], getString(devices[i],
								CL_DEVICE_NAME));
				list.add(worker);

			}
		}

		
		long before = System.nanoTime();
		for (Runnable worker : list) {
			executor.execute(worker);
		}
		// no new thread accepted
		executor.shutdown();
		try {
			// will wait till all threads finished, otherwise, will wait till
			// timeout
			executor.awaitTermination(60, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		long after = System.nanoTime();
		float totalDurationMs = (after - before) / 1e6f;
		System.out.println("Total duration: " + totalDurationMs + "ms");
            
//            long after = System.nanoTime();
//            float totalDurationMs = (after-before)/1e6f;
//            System.out.println("Total duration: "+totalDurationMs+"ms");
//            Thread[] threads;
//            group.enumerate();
    //
//            System.out.print(threads.length);
//            List<Thread> threads = getThreadsFor(Computation)


        
        }
        
       


//    Thread[] getGroupThreads( final ThreadGroup group ) {
//        if ( group == null )
//            throw new NullPointerException( "Null thread group" );
//        int nAlloc = group.activeCount( );
//        int n = 0;
//        Thread[] threads;
//        do {
//            nAlloc *= 2;
//            threads = new Thread[ nAlloc ];
//            n = group.enumerate( threads );
//        } while ( n == nAlloc );
//        return java.util.Arrays.copyOf( threads, n );
//    }

    /**
     * Returns the value of the device info parameter with the given name
     *
     * @param device The device
     * @param paramName The parameter name
     * @return The value
     */
    private static String getString(cl_device_id device, int paramName) {
        long size[] = new long[1];
        clGetDeviceInfo(device, paramName, 0, null, size);
        byte buffer[] = new byte[(int)size[0]];
        clGetDeviceInfo(device, paramName,
                buffer.length, Pointer.to(buffer), null);
        return new String(buffer, 0, buffer.length-1);

    }

    /**
     * Returns the value of the platform info parameter with the given name
     *
     * @param platform The platform
     * @param paramName The parameter name
     * @return The value
     */
    private static String getString(cl_platform_id platform, int paramName) {
        long size[] = new long[1];
        clGetPlatformInfo(platform, paramName, 0, null, size);
        byte buffer[] = new byte[(int)size[0]];
        clGetPlatformInfo(platform, paramName,
                buffer.length, Pointer.to(buffer), null);
        return new String(buffer, 0, buffer.length-1);
    }

    public static void main(String[] args) {
        int n = 150000;
        new MultiDeviceComputing().compute(n);


    }
}
